home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / technote / tchntstc.sit / Technical Notes Stack 3.2.1 / Technical Notes Stack 3.2.1 / card_28901.txt < prev    next >
Encoding:
Text File  |  1990-01-08  |  11.7 KB  |  304 lines

  1. -- card: 28901 from stack: in.1
  2. -- bmap block id: 0
  3. -- flags: 0000
  4. -- background id: 2711
  5. -- name: 088 Signals
  6.  
  7.  
  8. -- part contents for background part 9
  9. ----- text -----
  10. #088: Signals
  11.  
  12. See also:       Using Assembly Language (Mixing Pascal & Assembly)
  13.  
  14. Written by:     Rick Blair    August 1, 1986
  15. Updated:                      March 1, 1988
  16. _______________________________________________________________________________
  17.  
  18. Signals are a form of intra-program interrupt which can greatly aid clean, inexpensive error trapping in stack frame intensive languages. A program may invoke the Signal procedure and immediately return to the last invocation of CatchSignal, including the complete stack frame state at that point.
  19. _______________________________________________________________________________
  20.  
  21. Signals allow a program to leave off execution at one point and return control to a convenient error trap location, regardless of how many levels of procedure nesting are in between.
  22.  
  23. The example is provided with a Pascal interface, but it is easily adapted to other languages. The only qualification is that the language must bracket its procedures (or functions) with LINK and UNLK instructions. This will allow the signal code to clean up at procedure exit time by removing CatchSignal entries from its internal queue.
  24. Note: only procedures and/or functions that call CatchSignal need to be bracketed with LINK and UNLK instructions.
  25.  
  26. Important: InitSignals must be called from the main program so that A6 can be set up properly.
  27.  
  28. Note that there is no limit to the number of local CatchSignals which may occur within a single routine. Only the last one executed will apply, of course, unless you call FreeSignal. FreeSignal will ΓÇ£popΓÇ¥ off the last CatchSignal. If you attempt to Signal with no CatchSignals pending, Signal will halt the program with a debugger trap.
  29.  
  30. InitSignals creates a small relocatable block in the application heap to hold the signal queue. If CatchSignal is unable to expand this block (which it does 5 elements at a time), then it will signal back to the last successful CatchSignal with code = 200. A Signal(0) acts as a NOP, so you may pass OSErrs, for instance, after making File System type calls, and, if the OSErr is equal to NoErr, nothing will happen.
  31.  
  32. CatchSignal may not be used in an expression if the stack is used to evaluate that expression. For example, you canΓÇÖt write:
  33.  
  34.     c:= 3*CatchSignal;
  35.  
  36.  
  37. ΓÇ£GotchaΓÇ¥ summary
  38.  
  39.  1. Routines which call CatchSignal must have stack frames.
  40.  2. InitSignals must be called from the outermost (main) level.
  41.  3. DonΓÇÖt put the CatchSignal function in an expression. Assign the result to 
  42.     an INTEGER variable; i.e. i:=CatchSignal.
  43.  4. ItΓÇÖs safest to call a procedure to do the processing after CatchSignal 
  44.     returns. See the Pascal example TestSignals below. This will prevent the 
  45.     use of a variable which may be held in a register.
  46.  
  47. Below are three separate source files. First is the Pascal interface to the signaling unit, then the assembly language which implements it in MPW Assembler format. Finally, there is an example program which demonstrates the use of the routines in the unit.
  48.  
  49. {File ErrSignal.p}
  50. UNIT ErrSignal;
  51.  
  52. INTERFACE
  53.  
  54. {Call this right after your other initializations (InitGraf, etc.)--in other words as early as you can in the application}
  55. PROCEDURE InitSignals;
  56.  
  57. {Until the procedure which encloses this call returns, it will catch subsequent Signal calls, returning the code passed to Signal. When CatchSignal is encountered initially, it returns a code of zero. These calls may "nest"; i.e. you may have multiple CatchSignals in one procedure.
  58. Each nested CatchSignal call uses 12 bytes of heap space }
  59. FUNCTION CatchSignal:INTEGER;
  60.  
  61. {This undoes the effect of the last CatchSignal. A Signal will then invoke the CatchSignal prior to the last one.}
  62. PROCEDURE FreeSignal;
  63.  
  64. {Returns control to the point of the last CatchSignal. The program will then behave as though that CatchSignal had returned with the code parameter supplied to Signal.}
  65. PROCEDURE Signal(code:INTEGER);
  66.  
  67. END.
  68. {End of ErrSignal.p}
  69.  
  70.  
  71. HereΓÇÖs the assembly source for the routines themselves:
  72.  
  73. ; ErrSignal code w. InitSignal, CatchSignal,FreeSignal, Signal
  74. ; defined
  75. ;
  76. ;               Version 1.0 by Rick Blair
  77.  
  78.     PRINT    OFF
  79.     INCLUDE    'Traps.a'
  80.     INCLUDE    'ToolEqu.a'
  81.     INCLUDE    'QuickEqu.a'
  82.     INCLUDE    'SysEqu.a'
  83.     PRINT    ON
  84.                         
  85. CatchSigErr    EQU    200    ;"insufficient heap" message
  86. SigChunks    EQU    5     ;number of elements to expand by
  87. FrameRet    EQU    4     ;return addr. for frame (off A6)
  88. SigBigA6    EQU    $FFFFFFFF    ;maximum positive A6 value
  89.  
  90.  
  91. ; A template in MPW Assembler describes the layout of a collection of data 
  92. ; without actually allocating any memory space. A template definition starts ; with a RECORD directive and ends with an ENDR directive.
  93.  
  94. ; To illustrate how the template type feature works, the following template 
  95. ; is declared and used. By using this, the assembler source appromixates very ; closely Pascal source for referencing the corresponding information.
  96.  
  97. ;template for our table elements
  98. SigElement    RECORD    0    ;the zero is the template origin
  99. SigSP    DS.L    1    ;the SP at the CatchSignalΓÇö(DS.L just like EQU)
  100. SigRetAddr    DS.L    1    ;the address where the CatchSignal returned
  101. SigFRet    DS.L    1    ;return addr. for encl. procedure
  102. SigElSize    EQU    *    ;just like EQU 12
  103.     ENDR
  104.  
  105.  
  106. ; The global data used by these routines follows. It is in the form of a 
  107. ; RECORD, but, unlike above, no origin is specified, which means that memory 
  108. ; space *will* be allocated.
  109. ; This data is referenced through a WITH statement at the beginning of the 
  110. ; procs that need to get at this data. Since the Assembler knows when it is 
  111. ; referencing data in a data module (since they must be declared before they 
  112. ; are accessed), and since such data can only be accessed based on A5, there 
  113. ; is no need to explicitly specify A5 in any code which references the data 
  114. ; (unless indexing is used). Thus, in this program we have omitted all A5 
  115. ; references when referencing the data.
  116.  
  117. SigGlobals RECORD        ;no origin means this is a data record
  118.             ;not a template(as above)
  119. SigEnd    DS.L    1    ;current end of table
  120. SigNow    DS.L    1    ;the MRU element
  121. SigHandle    DC.L    0    ;handle to the table
  122.     ENDR                
  123.  
  124. InitSignals PROC    EXPORT    ;PROCEDURE InitSignals;
  125.                 IMPORT    CatchSignal
  126.     WITH    SigElement,SigGlobals
  127.  
  128. ;the above statement makes the template SigElement and the global data 
  129. ;record SigGlobals available to this procedure
  130.                 MOVE.L    #SigChunks*SigElSize,D0
  131.     _NewHandle    ;try to get a table
  132.     BNE.S    forgetit    ;we couldn't get that!?
  133.             
  134.     MOVE.L    A0,SigHandle    ;save it
  135.     MOVE.L    #-SigElSize,SigNow ;point "now" before start
  136.     MOVE.L    #SigChunks*SigElSize,SigEnd ;save the end
  137.     MOVE.L    #SigBigA6,A6    ;make A6 valid for Signal
  138. forgetit    RTS
  139.     ENDP
  140.  
  141. CatchSignal PROC    EXPORT    ;FUNCTION CatchSignal:INTEGER;    IMPORT    SiggySetup,Signal,SigDeath
  142.     WITH    SigElement,SigGlobals
  143.                 MOVE.L    (SP)+,A1    ;grab return address
  144.     MOVE.L    SigHandle,D0    ;handle to table
  145.     BEQ    SigDeath    ;if NIL then croak
  146.     MOVE.L    D0,A0    ;put handle in A-register
  147.     MOVE.L    SigNow,D0
  148.     ADD.L    #SigElSize,D0
  149.     MOVE.L    D0,SigNow    ;save new position
  150.     CMP.L    SigEnd,D0    ;have we reached the end?
  151.     BNE.S    catchit    ;no, proceed
  152.                 ADD.L    #SigChunks*SigElSize,D0 ;we'll try to expand
  153.     MOVE.L    D0,SigEnd    ;save new (potential) end
  154.     _SetHandleSize
  155.     BEQ.S    @0    ;jump around if it worked!
  156.             
  157. ;signals, we use 'em ourselves
  158.     MOVE.L    SigNow,SigEnd    ;restore old ending offset
  159.     MOVE.L    #SigElSize,D0
  160.     SUB.L    D0,SigNow    ;ditto for current position
  161.     MOVE.W    #catchSigErr,(SP);we'll signal a "couldn't
  162.                          ;                catch" error
  163.     JSR    Signal    ;never returns of course
  164.  
  165.  
  166. @0    MOVE.L    SigNow,D0
  167.             
  168. catchit    MOVE.L    (A0),A0    ;deref.
  169.     ADD.L    D0,A0    ;point to new entry
  170.     MOVE.L    SP,SigSP(A0)    ;save SP in entry
  171.     MOVE.L    A1,SigRetAddr(A0) ;save return address there
  172.     CMP.L    #SigBigA6,A6    ;are we at the outer level?
  173.     BEQ.S    @0    ;yes, no frame or cleanup needed 
  174.     MOVE.L    FrameRet(A6),SigFRet(A0);save old frame return
  175.                                 ;               address
  176.     LEA    SiggyPop,A0
  177.     MOVE.L    A0,FrameRet(A6) ;set cleanup code address
  178. @0    CLR.W    (SP)    ;no error code (before its time)
  179.     JMP    (A1)    ;done setting the trap
  180.             
  181. SiggyPop    JSR    SiggySetup    ;get pointer to element
  182.     MOVE.L    SigFRet(A0),A0 ;get proc's real return address
  183.     SUB.L    #SigElSize,D0
  184.     MOVE.L    D0,SigNow    ;"pop" the entry
  185.     JMP    (A0)    ;gone
  186.     ENDP
  187.  
  188. FreeSignal    PROC    EXPORT    ;PROCEDURE FreeSignal;
  189.     IMPORT    SiggySetup
  190.     WITH    SigElement,SigGlobals
  191.     JSR    SiggySetup    ;get pointer to current entry
  192.     MOVE.L    SigFRet(A0),FrameRet(A6) ;"pop" cleanup code
  193.     SUB.L    #SigElSize,D0
  194.     MOVE.L    D0,SigNow    ;"pop" the entry
  195.     RTS
  196.     ENDP
  197.  
  198. Signal    PROC    EXPORT    ;PROCEDURE Signal(code:INTEGER);
  199.     EXPORT    SiggySetup,SigDeath
  200.     WITH    SigElement,SigGlobals
  201.     MOVE.W    4(SP),D1    ;get code
  202.     BNE.S    @0    ;process the signal if code is non-zero
  203.     MOVE.L    (SP),A0    ;save return address
  204.     ADDQ.L    #6,SP    ;adjust stack pointer
  205.     JMP    (A0)    ;return to caller(code was 0)
  206.  
  207. @0    JSR    SiggySetup    ;get pointer to entry
  208.     BRA.S    SigLoop1
  209.             
  210. SigLoop    UNLK    A6    ;unlink stack by one frame
  211. SigLoop1    CMP.L    SigSP(A0),A6    ;is A6 beyond the saved stack?
  212.     BLO.S    SigLoop          ;yes, keep unlinking
  213.     MOVE.L   SigSP(A0),SP     ;bring back our SP
  214.     MOVE.L   SigRetAddr(A0),A0 ;get return address
  215.     MOVE.W   D1,(SP)          ;return code to CatchSignal
  216.     JMP      (A0)             ;Houston, boost the Signal!
  217.         ;(or Hooston if you're from the Negative Zone) 
  218.  
  219. SiggySetup    MOVE.L    SigHandle,A0
  220.     MOVE.L   (A0),A0    ;deref.
  221.     MOVE.L   A0,D0      ;to set CCR
  222.     BEQ.S    SigDeath   ;nil handle means trouble
  223.     MOVE.L   SigNow,D0  ;grab table offset to entry
  224.     BMI.S    SigDeath   ;if no entries then give up
  225.     ADD.L    D0,A0      ;point to current element
  226.     RTS
  227.  
  228. SigDeath    _Debugger        ;a signal sans catch is bad news
  229.  
  230.     ENDP
  231.     END
  232.  
  233.  
  234. Now for the example Pascal program:
  235.  
  236. PROGRAM TestSignals;
  237. USES ErrSignal;
  238.  
  239. VAR i:INTEGER;
  240.  
  241. PROCEDURE DoCatch(s:STR255; code:INTEGER);
  242. BEGIN
  243.   IF code<>0 THEN BEGIN
  244.     Writeln(s,code);
  245.     Exit(TestSignals);
  246.   END;
  247. END; {DoCatch}
  248.  
  249. PROCEDURE Easy;
  250.   PROCEDURE Never;
  251.     PROCEDURE DoCatch(s:STR255; code:INTEGER);
  252.     BEGIN
  253.       IF code<>0 THEN BEGIN
  254.         Writeln(s,code);
  255.         Exit(Never);
  256.       END;
  257.     END; {DoCatch}
  258.  
  259.   BEGIN {Never}
  260.   i:=CatchSignal;
  261.   DoCatch('Signal caught from Never, code = ', i );
  262.  
  263.   i:=CatchSignal;
  264.   IF i<>0 THEN DoCatch('Should never get here!',i);
  265.        
  266.   FreeSignal; {"free" the last CatchSignal}
  267.   Signal(7); {Signal a 7 to the last CatchSignal}
  268.   END;{Never}
  269. BEGIN {Easy}
  270. Never;
  271. Signal(69);     {this won't be caught in Never}
  272. END;{Easy}    {all local CatchSignals are freed when a procedure exits.}
  273.  
  274. BEGIN {PROGRAM}
  275. InitSignals; {You must call this early on!}
  276.  
  277. {catch Signals not otherwise caught by the program}
  278. i:=CatchSignal;
  279. IF i<>0 THEN
  280.  DoCatch('Signal caught from main, code = ',i);
  281.  
  282. Easy;
  283. END.
  284.  
  285.  
  286. The example program produces the following two lines of output:
  287.  
  288.  Signal caught from Never, code = 7
  289.  Signal caught from main, code = 69
  290.  
  291.  
  292.  
  293.  
  294. -- part contents for background part 2
  295. ----- text -----
  296. 088
  297.  
  298. -- part contents for background part 7
  299. ----- text -----
  300. Signals
  301.  
  302. -- part contents for background part 113
  303. ----- text -----
  304. Using Assembly Language